/*
 * tetrixboard.cpp - the place where all the action happens
 * Copyright (C) 2006-2007  Mithin Kumar L
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this application; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../include/tetrixboard.h"

#include <QKeyEvent>
#include <QPainter>
#include <QPainterPath>

#include <math.h>

TetrixBoard::TetrixBoard(QWidget *parent) : QFrame(parent)
{
	setFocusPolicy(Qt::StrongFocus);
	setAutoFillBackground(true);
	
	initializeColorMap();

	theta = 0;
	isStarted = false;
    isPaused = false;
    clearBoard();
	curX = -2;
	nextPiece.setRandomShape();
	start();
}

QSize TetrixBoard::sizeHint() const
{
    return QSize(BoardWidth * 15 + frameWidth() * 2,
                 BoardHeight * 15 + frameWidth() * 2);
}

QSize TetrixBoard::minimumSizeHint() const
{
    return QSize(BoardWidth * 5 + frameWidth() * 2,
                 BoardHeight * 5 + frameWidth() * 2);
}

void TetrixBoard::start()
{
    if (isPaused)
        return;

    isStarted = true;
    isWaitingAfterLine = false;

    newPiece();
    timer.start(timeoutTime(), this);
}

void TetrixBoard::setGamePaused(bool p)
{
	isPaused = p;
}

void TetrixBoard::newPiece()
{
    curPiece = nextPiece;
    nextPiece.setRandomShape();
	
	if(curPiece.shape() == SquareShape){
		theta-=0.25;
	}
	else
		theta-=0.5;

    curY = 16;
	repaint();
}

int TetrixBoard::squareWidth()
{
	return contentsRect().width() / BoardWidth;
}

int TetrixBoard::squareHeight()
{
	return contentsRect().height() / BoardHeight;
}

TetrixShape &TetrixBoard::shapeAt(int x,int y)
{
	 return board[x][y];	
}

int TetrixBoard::timeoutTime()
{
	//Need to change this in order to accomodate levels.
	return 1000;
}

void TetrixBoard::clearBoard()
{
	for (int i = 0; i < BoardWidth; ++i){
		for(int j = 0; j < BoardHeight; ++j){
			board[i][j] = NoShape;
		}
	}
}

bool TetrixBoard::movePiece(int xDir,int yDir)
{
	if(isPaused)
		return false;

	for(int i =0; i < 4; ++i){
		if(curY - curPiece.y(i) + yDir < 0 ||
			board[((curX + curPiece.x(i) + xDir)%15+15)%15][curY - curPiece.y(i) + yDir] != NoShape){			
				if(yDir < 0){
					//Write code to place the piece on the board.
					placePiece();
				}
				return false;
		}
	}

	if(xDir != 0){
		theta += xDir;
		theta = fmod(fmod(theta,15.0)+15.0,15.0);
		curX += xDir;
	}
	else{
		curY += yDir;
		repaint();
	}
	return true;
}

void TetrixBoard::placePiece()
{
	for(int i = 0; i < 4; ++i){
		board[((curX+curPiece.x(i))%15+15)%15][curY-curPiece.y(i)] = curPiece.shape();
	}
	int curRingsRemoved = 0;

	for(int j = 0; j < 15; ++j){
		int k;
		for(k = 0; k < 15; ++k){
			if(board[k][j] == NoShape)
				break;
		}
		if(k == 15){
			for(int i = j; i < 15; ++i){
				for(k = 0; k < 15; ++k){
					if(i < 14){
						board[k][i] = board[k][i+1];
					}
					else{
						board[k][i] = NoShape;
					}
				}
			}
			--j;
			++curRingsRemoved;
		}
	}

	if(curRingsRemoved > 0){
		emit ringsRemoved(curRingsRemoved);
	}

	if(curPiece.shape() == SquareShape)
		theta+=0.25;
	else
		theta+=0.5;

	newPiece();

	if(overlaps()){
		//Game Over
	}
}

bool TetrixBoard::overlaps()
{
	int X,Y;

	for(int i = 0;i < 4; ++i){
		X = ((curX + curPiece.x(i))%15+15)%15;
		Y = curY - curPiece.y(i);

		if( Y < 0 || board[X][Y] != NoShape){
			return true;
		}
	}
	return false;
}

void TetrixBoard::paintEvent(QPaintEvent *)
{
	QPainter painter(this);
	painter.drawImage(QRect(5,333,250,55),QImage(":/images/base.png"));

	painter.setRenderHint(QPainter::Antialiasing);
	painter.setWindow(-100,-150,200,350);
	

	drawBoard(&painter);

	if (curPiece.shape() != NoShape) {
		QColor color = colorMap.value(curPiece.shape());
		color.setAlphaF(0.9);
		painter.setBrush(color);
		painter.setPen(QColor(0,0,0));

		int x = curX;
		int y = curY;

        for (int i = 0; i < 4; ++i) {
                        
			drawFront(x+curPiece.x(i),y-curPiece.y(i),false,&painter);
			
			if(curPiece.x(i) == 0 && board[((x + curPiece.x(i)+1)%15+15)%15][y-curPiece.y(i)] == NoShape){
				int j;
				for(j = 0; j < 4; ++j){
					if(curPiece.x(j) == 1 && curPiece.y(j) == curPiece.y(i)){
						break;
					}
				}
                if(j == 4){
					drawSide(x + curPiece.x(i)+1,y-curPiece.y(i),&painter);
				}
			}

			if(curPiece.x(i) >= 2 && board[((x + curPiece.x(i)-1)%15+15)%15][y-curPiece.y(i)] == NoShape){
				int j;
				for(j = 0; j < 4; ++j){
					if(curPiece.x(j) == curPiece.x(i)-1 && curPiece.y(j) == curPiece.y(i)){
						break;
					}
				}
                if(j == 4){
					drawSide(x + curPiece.x(i),y-curPiece.y(i),&painter);
				}
			}

			if(board[((x + curPiece.x(i))%15+15)%15][y-(curPiece.y(i)+1)] == NoShape){
				int j;
				for(j = 0; j < 4; ++j){
					if(curPiece.x(j) == curPiece.x(i) && curPiece.y(j)+1 == curPiece.y(i)){
						break;
					}
				}
				if(j == 4){
					drawTop(x+curPiece.x(i),y-curPiece.y(i),&painter);
				}
			}
        }
    }
}

void TetrixBoard::drawBoard(QPainter *painter)
{
	int xOffset = ((((int)theta%15)+15)%15)>>0;
	int tempX = 0;
	static const xArr[15] = {7,8,6,9,5,10,4,11,3,12,2,13,1,14,0};
	
	for(int i = 0; i < 15; ++i){
		tempX = xArr[i];
		for(int y = 0; y < 15; ++y){
			double intensity = 0.2 + 0.8 * i/14;
			if(board[((tempX + xOffset)%15+15)%15][y] != NoShape){
				QColor color = colorMap.value(board[((tempX + xOffset)%15+15)%15][y]);
				color.setRed((int)(color.red() * intensity));
				color.setGreen((int)(color.green() * intensity));
				color.setBlue((int)(color.blue() * intensity));
				painter->setBrush(color);
				painter->setPen(QColor(0,0,0));
				drawFront(tempX + xOffset,y,i < (7 + int(fmod(fmod(theta,1.0)+1.0,1))),painter);

				if(board[((tempX + xOffset)%15+15)%15][y+1] == NoShape){
					drawTop(tempX + xOffset,y,painter);
				}

				if(i%2 != 0 || i == 0 && fmod(fmod(theta,1.0)+1.0,1.0) != 0.75){
					if(board[((tempX+1+xOffset)%15+15)%15][y] == NoShape){
						drawSide(tempX+1+xOffset,y,painter);
					}
				}

				if(i%2 == 0 && i!= 14 || ( fmod(fmod(theta,1.0)+1.0,1.0) == 0.75 && i == 1)){
					if(board[((tempX-1+xOffset)%15+15)%15][y] == NoShape){
						drawSide(tempX + xOffset,y,painter);
					}
				}
			}
		}
	}
}

void TetrixBoard::drawFront(int x,int y,bool r,QPainter *painter)
{
	double tempX = x;
	double tempY = y;

	QPainterPath path;

	tempY -= 1;
	findPosition(tempX,tempY,r);
	path.moveTo(tempX,tempY);

	tempX = x;
	tempY = y;
	findPosition(tempX,tempY,r);
	path.lineTo(tempX,tempY);

	tempX = x + 1;
	tempY = y;
	findPosition(tempX,tempY,r);
	path.lineTo(tempX,tempY);

	tempX = x+1;
	tempY = y-1;
	findPosition(tempX,tempY,r);
	path.lineTo(tempX,tempY);

	path.closeSubpath();

	painter->drawPath(path);
}

void TetrixBoard::drawTop(int x,int y,QPainter *painter)
{
	double tempX = x;
	double tempY = y;

	QPainterPath path;

	findPosition(tempX,tempY,false);
	path.moveTo(tempX,tempY);

	tempX = x;
	tempY = y;

	findPosition(tempX,tempY,true);
	path.lineTo(tempX,tempY);

	tempX = x+1;
	tempY = y;

	findPosition(tempX,tempY,true);
	path.lineTo(tempX,tempY);

	tempX = x+1;
	tempY = y;

	findPosition(tempX,tempY,false);
	path.lineTo(tempX,tempY);

	path.closeSubpath();

	painter->drawPath(path);
}

void TetrixBoard::drawSide(int x,int y,QPainter *painter)
{
	double tempX = x;
	double tempY = y;

	QPainterPath path;

	findPosition(tempX,tempY,false);
	path.moveTo(tempX,tempY);

	tempX = x;
	tempY = y;

	findPosition(tempX,tempY,true);
	path.lineTo(tempX,tempY);

	tempX = x;
	tempY = y-1;

	findPosition(tempX,tempY,true);
	path.lineTo(tempX,tempY);

	tempX = x;
	tempY = y-1;

	findPosition(tempX,tempY,false);
	path.lineTo(tempX,tempY);

	path.closeSubpath();

	painter->drawPath(path);
}

void TetrixBoard::findPosition(double &x,double &y,bool r)
{
	double tempX = x;
	double tempY = y;

	double M_PI = 3.141592653589793;

	double roundness;
	if(r)
		roundness = 40;
	else
		roundness = 60;

	x = roundness*cos((2.0*M_PI)*((tempX-theta)/15.0-1.0/4.0))*(tempY+30.0)/30.0;
	y = 150.0-tempY*20.0 - 0.3*roundness*sin((2.0*M_PI)*((tempX-theta)/15.0-1.0/4.0));
}

void TetrixBoard::keyPressEvent(QKeyEvent *event)
{
	if (!isStarted || isPaused || curPiece.shape() == NoShape) {
		QWidget::keyPressEvent(event);
		return;
    }
	int direction = 0;

    switch (event->key()) {
		case Qt::Key_Left:
			movePiece(1,0);
			break;
		case Qt::Key_Right:
			movePiece(-1,0);
			break;
		case Qt::Key_Down:
			movePiece(0,-1);
			break;
		case Qt::Key_Up:
			direction = 1;
			//curPiece.rotate(1);
			//tryMove(curPiece.rotatedLeft(), curX, curY);
			break;
		case Qt::Key_Space:
			//dropDown();
			break;
		case Qt::Key_D:
			//oneLineDown();
			break;
		default:
			QWidget::keyPressEvent(event);
	}
	if(direction != 0){
		curPiece.rotate(direction);
		if(overlaps()){
			if(movePiece(1,0) || movePiece(-1,0)){
				return;
			}
			else{
				curPiece.rotate(-direction,true);
			}
		}
	}
	repaint();
}

void TetrixBoard::timerEvent(QTimerEvent *event)
{
	if (event->timerId() == timer.timerId()) {
        if (isWaitingAfterLine) {
			isWaitingAfterLine = false;
			newPiece();
			timer.start(timeoutTime(), this);
		} else {
			movePiece(0,-1);
		}
	} else {
		QWidget::timerEvent(event);
	}
}

void TetrixBoard::initializeColorMap()
{
	colorMap[SquareShape] = QColor(29,182,226);
	colorMap[LineShape] = QColor(193,62,157);
	colorMap[TShape] = QColor(150,203,52);
	colorMap[LShape] = QColor(244,248,105);
	colorMap[MirroredLShape] = QColor(117,111,242);
	colorMap[ZShape] = QColor(192,186,160);
	colorMap[SShape] = QColor(247,128,106);
}